/*
 * [y] hybris Platform
 *
 * Copyright (c) 2000-2012 hybris AG
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of hybris
 * ("Confidential Information"). You shall not disclose such Confidential
 * Information and shall use it only in accordance with the terms of the
 * license agreement you entered into with hybris.
 * 
 *  
 */
package de.hybris.merchandise.initialdata.setup;

import de.hybris.merchandise.core.setup.CoreSystemSetup;
import de.hybris.merchandise.initialdata.constants.MerchandiseInitialDataConstants;
import de.hybris.platform.acceleratorservices.setup.AbstractSystemSetup;
import de.hybris.platform.core.Registry;
import de.hybris.platform.core.initialization.SystemSetup;
import de.hybris.platform.core.initialization.SystemSetup.Process;
import de.hybris.platform.core.initialization.SystemSetup.Type;
import de.hybris.platform.core.initialization.SystemSetupContext;
import de.hybris.platform.core.initialization.SystemSetupParameter;
import de.hybris.platform.core.initialization.SystemSetupParameterMethod;
import de.hybris.platform.servicelayer.cronjob.PerformResult;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;


/**
 * This class provides hooks into the system's initialization and update processes.
 * 
 * @see "https://wiki.hybris.com/display/release4/Hooks+for+Initialization+and+Update+Process"
 */
@SystemSetup(extension = MerchandiseInitialDataConstants.EXTENSIONNAME)
public class InitialDataSystemSetup extends AbstractSystemSetup
{
	private static final Logger LOG = Logger.getLogger(InitialDataSystemSetup.class);

	private static final String IMPORT_SAMPLE_DATA = "importSampleData";
	private static final String SAMPLE_DATA_IMPORT_FOLDER = "acceleratorsampledata";
	private static final String IMPORT_MERCHANDISE_DATA = "importHybrisData";

	private String productCatalogNameForRerun = null; //null indicates no rerun needed

	/**
	 * Generates the Dropdown and Multi-select boxes for the project data import
	 */
	@Override
	@SystemSetupParameterMethod
	public List<SystemSetupParameter> getInitializationOptions()
	{
		final List<SystemSetupParameter> params = new ArrayList<SystemSetupParameter>();

		params.add(createBooleanSystemSetupParameter(IMPORT_SAMPLE_DATA, "Import Sample Data", true));
		params.add(createBooleanSystemSetupParameter(CoreSystemSetup.ACTIVATE_SOLR_CRON_JOBS, "Activate Solr Cron Jobs", true));
		// Add more Parameters here as your require
		params.add(createBooleanSystemSetupParameter(IMPORT_MERCHANDISE_DATA, "Import Hybris Merchandise Sample Data", true));

		return params;
	}

	/**
	 * Implement this method to create initial objects. This method will be called by system creator during
	 * initialization and system update. Be sure that this method can be called repeatedly.
	 * 
	 * @param context
	 *           the context provides the selected parameters and values
	 */
	@SystemSetup(type = Type.ESSENTIAL, process = Process.ALL)
	public void createEssentialData(final SystemSetupContext context)
	{
		// Add Essential Data here as you require
	}

	/**
	 * Implement this method to create data that is used in your project. This method will be called during the system
	 * initialization.
	 * 
	 * @param context
	 *           the context provides the selected parameters and values
	 */
	@SystemSetup(type = Type.PROJECT, process = Process.ALL)
	public void createProjectData(final SystemSetupContext context)
	{
		// Add Store imports here as you require
		// importCommonData(context, "merchandiseinitialdata");
		// This would import a standard store: (one basestore, one cmssite, one product catalog, one content catalog)
		// importStoreInitialData(context, "merchandiseinitialdata", "yaccelerator", "yaccelerator", Collections.singletonList("yaccelerator"));

		if (getBooleanSystemSetupParameter(context, IMPORT_SAMPLE_DATA))
		{
			importCommonData(context, SAMPLE_DATA_IMPORT_FOLDER);
			importStoreInitialData(context, SAMPLE_DATA_IMPORT_FOLDER, "electronics", "electronics",
					Collections.singletonList("electronics"));
			importStoreInitialData(context, SAMPLE_DATA_IMPORT_FOLDER, "apparel-uk", "apparel",
					Collections.singletonList("apparel-uk"));
			importStoreInitialData(context, SAMPLE_DATA_IMPORT_FOLDER, "apparel-de", "apparel",
					Collections.singletonList("apparel-de"));
		}

		if (getBooleanSystemSetupParameter(context, IMPORT_MERCHANDISE_DATA))
		{
			logInfo(context, "Importing Hybris site content...");
			importCommonData(context, "merchandiseinitialdata");
			importStoreInitialData(context, "merchandiseinitialdata", "hybris", "hybris", Collections.singletonList("hybris"));
		}
	}

	/**
	 * Use this method to import a standard setup store.
	 * 
	 * @param context
	 *           the context provides the selected parameters and values
	 * @param storeName
	 *           the name of the store
	 * @param productCatalog
	 *           the name of the product catalog
	 * @param contentCatalogs
	 *           the list of content catalogs
	 */
	protected void importStoreInitialData(final SystemSetupContext context, final String importDirectory, final String storeName,
			final String productCatalog, final List<String> contentCatalogs)
	{
		logInfo(context, "Begin importing store [" + storeName + "]");

		importProductCatalog(context, importDirectory, productCatalog);

		for (final String contentCatalog : contentCatalogs)
		{
			importContentCatalog(context, importDirectory, contentCatalog);
		}

		logInfo(context, "Begin importing advanced personalization rules for [" + storeName + "]");

		final String importRoot = "/" + importDirectory + "/import";

		importImpexFile(context, importRoot + "/stores/" + storeName + "/btg.impex", false);

		//create jobs
		synchronizeProductCatalog(context, productCatalog, false);
		for (final String contentCatalog : contentCatalogs)
		{
			synchronizeContentCatalog(context, contentCatalog, false);
		}
		assignDependent(context, productCatalog, contentCatalogs);
		//perform jobs
		synchronizeProductCatalog(context, productCatalog, true);

		logInfo(context, "Begin SOLR re-index [" + productCatalog + "]");
		executeSolrIndexerCronJob(productCatalog + "Index", true);
		logInfo(context, "Done SOLR re-index [" + productCatalog + "]");

		for (final String contentCatalog : contentCatalogs)
		{
			synchronizeContentCatalog(context, contentCatalog, true);
		}


		logInfo(context, "Begin importing store [" + storeName + "]");

		importImpexFile(context, importRoot + "/stores/" + storeName + "/points-of-service-media.impex", false);
		importImpexFile(context, importRoot + "/stores/" + storeName + "/points-of-service.impex", false);

		logInfo(context, "Done importing store [" + storeName + "]");

		if (getBooleanSystemSetupParameter(context, CoreSystemSetup.ACTIVATE_SOLR_CRON_JOBS))
		{
			logInfo(context, "Activating SOLR index job for [" + productCatalog + "]");
			activateSolrIndexerCronJobs(productCatalog + "Index");
		}
	}

	protected void importProductCatalog(final SystemSetupContext context, final String importDirectory, final String catalogName)
	{
		logInfo(context, "Begin importing Product Catalog [" + catalogName + "]");

		// Load Units
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/classifications-units.impex", false);

		// Load Categories
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/categories.impex", false);
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/categories-media.impex", false);
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/categories-classifications.impex", false);

		// Load Suppliers
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/suppliers.impex", false);
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/suppliers-media.impex", false);

		// Load Products
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName + "ProductCatalog/products.impex");
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/variant-products.impex", false);
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/products-media.impex", false);
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/products-classifications.impex", false);

		// Load Products Relations
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/products-relations.impex", false);

		// Load Prices
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/products-prices.impex", false);

		// Load Promotions
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/promotions.impex", false);

		// Update SOLR Index
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName + "ProductCatalog/solr.impex",
				false);

		// Load Stock Levels
		importImpexFile(context, "/" + importDirectory + "/import/productCatalogs/" + catalogName
				+ "ProductCatalog/products-stocklevels.impex", false);
	}



	protected void synchronizeProductCatalog(final SystemSetupContext context, final String catalogName, final boolean sync)
	{
		logInfo(context, "Begin synchronizing Product Catalog [" + catalogName + "] - "
				+ (sync ? "synchronizing" : "initializing job"));

		createProductCatalogSyncJob(context, catalogName + "ProductCatalog");

		if (sync)
		{
			final PerformResult syncCronJobResult = executeCatalogSyncJob(context, catalogName + "ProductCatalog");

			if (isSyncRerunNeeded(syncCronJobResult))
			{
				this.productCatalogNameForRerun = catalogName;
				logInfo(context, "Product catalog sync has issues. Setting the flag to re-run the sync");
			}
		}

		logInfo(context, "Done " + (sync ? "synchronizing" : "initializing job") + " Product Catalog [" + catalogName + "]");
	}


	protected void importContentCatalog(final SystemSetupContext context, final String importDirectory, final String catalogName)
	{
		logInfo(context, "Begin importing Content Catalog [" + catalogName + "]");

		final String importRoot = "/" + importDirectory + "/import";

		importImpexFile(context, importRoot + "/contentCatalogs/" + catalogName + "ContentCatalog/cms-content.impex", false);
		importImpexFile(context, importRoot + "/contentCatalogs/" + catalogName + "ContentCatalog/cms-mobile-content.impex", false);
		importImpexFile(context, importRoot + "/contentCatalogs/" + catalogName + "ContentCatalog/email-content.impex", false);

		logInfo(context, "Done importing Content Catalog [" + catalogName + "]");
	}

	protected void synchronizeContentCatalog(final SystemSetupContext context, final String catalogName, final boolean sync)
	{
		logInfo(context, "Begin synchronizing Content Catalog [" + catalogName + "] - "
				+ (sync ? "synchronizing" : "initializing job"));

		createContentCatalogSyncJob(context, catalogName + "ContentCatalog");

		if (sync)
		{
			executeCatalogSyncJob(context, catalogName + "ContentCatalog");
		}

		if (sync && StringUtils.isNotEmpty(this.productCatalogNameForRerun))
		{
			logInfo(context, "Rerunnig product catalog synchronization for  [" + this.productCatalogNameForRerun + "]");
			if (isSyncRerunNeeded(executeCatalogSyncJob(context, this.productCatalogNameForRerun + "ProductCatalog")))
			{
				logInfo(context, "Rerunnig product catalog synchronization for [" + this.productCatalogNameForRerun
						+ "], failed   please consult logs for more details.");
			}
			this.productCatalogNameForRerun = null;
		}

		logInfo(context, "Done " + (sync ? "synchronizing" : "initializing job") + " Content Catalog [" + catalogName + "]");


	}

	/**
	 * Imports Common Data
	 */
	protected void importCommonData(final SystemSetupContext context, final String importDirectory)
	{
		logInfo(context, "Importing Common Data...");

		final String importRoot = "/" + importDirectory + "/import";

		importImpexFile(context, importRoot + "/common/user-groups.impex", false);
		importImpexFile(context, importRoot + "/common/promotions.impex", false);

		final List<String> extensionNames = Registry.getCurrentTenant().getTenantSpecificExtensionNames();
		if (extensionNames.contains("cmscockpit"))
		{
			importImpexFile(context, importRoot + "/cockpits/cmscockpit/cmscockpit-users.impex");
		}

		if (extensionNames.contains("productcockpit"))
		{
			importImpexFile(context, importRoot + "/cockpits/productcockpit/productcockpit-users.impex");
		}

		if (extensionNames.contains("cscockpit"))
		{
			importImpexFile(context, importRoot + "/cockpits/cscockpit/cscockpit-users.impex");
		}

		if (extensionNames.contains("reportcockpit"))
		{
			importImpexFile(context, importRoot + "/cockpits/reportcockpit/reportcockpit-users.impex");
			importImpexFile(context, importRoot + "/cockpits/reportcockpit/reportcockpit-mcc-links.impex");
		}
	}


	private void assignDependent(final SystemSetupContext context, final String dependsOnProduct,
			final List<String> dependentContents)
	{
		if (CollectionUtils.isNotEmpty(dependentContents) && StringUtils.isNotBlank(dependsOnProduct))
		{
			final Set<String> dependentSyncJobsNames = new HashSet<String>();
			for (final String content : dependentContents)
			{
				dependentSyncJobsNames.add(content + "ContentCatalog");
			}

			getSetupSyncJobService().assignDependentSyncJobs(dependsOnProduct + "ProductCatalog", dependentSyncJobsNames);
		}

	}

}
